﻿////-----------------------------------------------------------
//// This is a sample program for the Visual Studio 2017 CANlib tutorial.
//// It prints a list of connected CAN interfaces.
////
//// For further information please refer to the tutorial section of the CANlib documentation.
////-----------------------------------------------------------
//
//
//#include "canlib.h"
//
//#include "iostream"
//
//// When called, CheckForError will check for and print any error.
//// The program is aborted if an error has occured.
//void CheckForError(char cmd[50], canStatus stat)
//{
//    //if stat not ok, print error
//    if (stat != canOK)
//    {
//        char buf[255];
//        buf[0] = '\0';
//        canGetErrorText(stat, buf, sizeof(buf));
//
//        printf("[%s] %s: failed, stat=%d\n", cmd, buf, (int)stat);
//        printf("ABORT (Press any key)");
//
//        //getchar();
//        abort();
//    }
//}
//
//// ListChannels prints a list of all connected CAN interfaces.
//void ListChannels()
//{
//    canStatus stat;
//    int number_of_channels;
//
//    int device_channel;
//    char device_name[255];
//
//    // Get number of channels
//    stat = canGetNumberOfChannels(&number_of_channels);
//    CheckForError((char*)"canGetNumberOfChannels", stat);
//
//    if (number_of_channels > 0) {
//        printf("%s %d %s\n", "Found", number_of_channels, "channels");
//    }
//    else {
//        printf("Could not find any CAN interface.\n");
//    }
//
//    // Loop and print all channels
//    for (int i = 0; i < number_of_channels; i++) {
//        stat = canGetChannelData(i, canCHANNELDATA_DEVDESCR_ASCII, device_name, sizeof(device_name));
//        CheckForError((char*)"canGetChannelData", stat);
//
//        stat = canGetChannelData(i, canCHANNELDATA_CHAN_NO_ON_CARD, &device_channel, sizeof(device_channel));
//        CheckForError((char*)"canGetChannelData", stat);
//
//        printf("%s %d %s %d\n", "Found channel:", i, device_name, (device_channel + 1));
//    }
//}
//
//int main()
//{
//    //canStatus stat;
//
//    canInitializeLibrary();
//
//    printf("%s\n", "CanlibTutorial started");
//
//    ListChannels();
//
//    return 0;
//}

#include <chrono>
#include <iostream>
#include <thread>
#include <stdio.h>
#include <mutex>
#include <condition_variable>
#include <iostream>
using namespace std;

// To use CANlib, we need to include canlib.h and also link to canlib32.lib
// when compiling.
#include "canlib.h"
// When called, CheckForError will check for and print any error.
// The program is aborted if an error has occured.
#include <stdio.h>
#include <conio.h>


std::mutex mtx;
std::condition_variable cv;
bool ready = false;


void CheckForError(char cmd[50], canStatus stat)
{

    //if stat not ok, print error
    if (stat != canOK)
    {
        char buf[255];
        buf[0] = '\0';
        canGetErrorText(stat, buf, sizeof(buf));
        printf("[%s] %s: failed, stat=%d\n", cmd, buf, (int)stat);
        printf("ABORT (Press any key)");
        getchar();
        abort();
    }
}
// The check method takes a canStatus (which is an enumerable) and the method
// name as a string argument. If the status is an error code, it will print it.
// Most Canlib method return a status, and checking it with a method like this
// is a useful practice to avoid code duplication.
void Check(const char* id, canStatus stat)
{
    if (stat != canOK) {
        char buf[50];
        buf[0] = '\0';
        canGetErrorText(stat, buf, sizeof(buf));
        printf("%s: failed, stat=%d (%s)\n", id, (int)stat, buf);
    }
}

void dumpMessageLoop(canHandle hnd, int channel_number) {
    // First declare some variables for holding the incoming messages. The
    // incoming messages consist of the same parameters as an outgoing message,
    // i.e. identifier (id), body (msg), length (dlc), and flags), as well as a
    // timestamp.
    canStatus stat = canOK;
    long id;
    unsigned int dlc, flags;
    //	unsigned char msg[8]; CAN
    unsigned char msg[64]; //CAN FD
    DWORD timestamp;
    //printf("[Recving ]Listening for messages on channel %d, press any key to close\n", channel_number);
    // Start a loop that loops until a key is pressed.
    while (!_kbhit()) {
        // Call the canReadWait method to wait for a message on the channel. This
        // method has a timeout parameter which in this case is set to 100 ms. If a
        // message is received during this time, it will return the status code
        // canOK and the message will be written to the output parameters. If no
        // message is received, it will return canERR_NOMSG.
        stat = canReadWait(hnd, &id, msg, &dlc, &flags, &timestamp, 100);
        // Check that the returned status is OK (which means that a message has been received)
        if (stat == canOK) {
            // If the message contains an error flag (which implies a different kind
            // of error than if an error signal had been returned), an error message
            // will be shown.
            if (flags & canMSG_ERROR_FRAME) {
                printf("***ERROR FRAME RECEIVED***");
            }
            // If no error flag was found, the program prints the message.
            else {
                printf("[Recving..Id: %ld, Dlc: %u",id,dlc );
                for (int i = 0; i < 64; i++)
                {
                    printf(" %u ", msg[i]);
                }
                printf(" %u ]\n",timestamp);
            }
        }
        // Break the loop if something goes wrong, i.e. if we get a status code
        // that is not canOK (taken care of above) and not canERR_NOMSG
        else if (stat != canERR_NOMSG) {
            Check("canRead", stat);
            break;
        }
    }
}

void SendMessageCan()
{
    while (1)
    {
        cout << "[SendMessage........]\n";
        cout << "Hello from SendMessage thread " << this_thread::get_id() << endl << endl;
        // simulate expensive operation
         //std::this_thread::sleep_for(std::chrono::seconds(1));
         // Holds a handle to the CAN channel
        canHandle hnd;
        // Status returned by the Canlib calls
        canStatus stat;
        // The CANlib channel number we would like to use
        int channel_number = 0;
        // The msg will be the body of the message we send on the CAN bus.
        //char msg[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };  CAN 
        //CAN FD
        char msg[64] = { 1, 2, 3, 4, 5, 6, 7, 8 ,
        1, 2, 3, 4, 5, 6, 7, 8 , 
        1, 2, 3, 4, 5, 6, 7, 8 , 
        1, 2, 3, 4, 5, 6, 7, 8 , 
        1, 2, 3, 4, 5, 6, 7, 8 , 
        1, 2, 3, 4, 5, 6, 7, 8 , 
        1, 2, 3, 4, 5, 6, 7, 8 , 
        1, 2, 3, 4, 5, 6, 7, 8};
      /** for (int i = 0; i < 64; i++)
        {
            if (i % 8 == 0)
                printf("\n");
            printf(" %u ", msg[i]);
        }
        printf("\n");*/
        // The first thing we need to do is to initialize the Canlib library. This
        // always needs to be done before doing anything with the library.
        canInitializeLibrary();
        cout << "[Sending..]Opening channel->" << channel_number << endl;
        // Next, we open up the channel and receive a handle to it. Depending on what
        // devices you have connected to your computer, you might want to change the
        // channel number. The canOPEN_ACCEPT_VIRTUAL flag means that it is ok to
        // open the selected channel, even if it is on a virtual device.
        //hnd = canOpenChannel(channel_number, canOPEN_ACCEPT_VIRTUAL);// CAN
        hnd = canOpenChannel(channel_number, canOPEN_ACCEPT_VIRTUAL|canOPEN_CAN_FD | canOPEN_EXCLUSIVE | canOPEN_REQUIRE_EXTENDED); //CAN FD
        // If the call to canOpenChannel is successful, it will return an integer
        // which is greater than or equal to zero. However, is something goes wrong,
        // it will return an error status which is a negative number.
        if (hnd < 0) {
            // To check for errors and print any possible error message, we can use the
            // Check method.
            Check("canOpenChannel", (canStatus)hnd);
            // and then exit the program.
            exit(1);
        }
        //printf("Setting bitrate and going bus on\n");
        // Once we have successfully opened a channel, we need to set its bitrate. We
        // do this using canSetBusParams. CANlib provides a set of predefined bus parameter
        // settings in the form of canBITRATE_XXX constants. For other desired bus speeds
        // bus paramters have to be set manually.
        // See CANlib documentation for more information on parameter settings.

        //CAN
        //stat = canSetBusParams(hnd, canBITRATE_250K, 0, 0, 0, 0, 0);
        //Check("canSetBusParams", stat);

        //CAN FD
        stat = canSetBusParams(hnd, canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0);
        Check("canSetBusParams", stat);
        stat = canSetBusParamsFd(hnd, canFD_BITRATE_1M_80P, 0, 0, 0);
        Check("canSetBusParamsFD", stat);


        // Next, take the channel on bus using the canBusOn method. This needs to be
        // done before we can send a message.
        stat = canBusOn(hnd);
        Check("canBusOn", stat);
        //printf("Writing a message to the channel and waiting for it to be sent \n");
        // We send the message using canWrite. This method takes five parameters:
        // the channel handle, the message identifier, the message body, the message
        // length (in bytes) and optional flags.
        
        //CAN
        //stat = canWrite(hnd, 123, msg, 8, 0);


        //CAN FD
        stat = canWrite(hnd, 123, msg, 64, canFDMSG_FDF);
        Check("canWrite", stat);
        // After sending, we wait for at most 100 ms for the message to be sent, using
        // canWriteSync.
        stat = canWriteSync(hnd, 100);
        Check("canWriteSync", stat);
        //printf("Going off bus and closing channel");
        // Once we are done using the channel, we go off bus using the
        // canBusOff method. It take the handle as the only argument.
        stat = canBusOff(hnd);
        Check("canBusOff", stat);
        // We also close the channel using the canCloseChannel method, which take the
        // handle as the only argument.
        stat = canClose(hnd);
        Check("canClose", stat);
        //cout << "[Send succefully......]\n\n";
        std::this_thread::sleep_for(std::chrono::seconds(1));
        ready = true;
        cv.notify_one();
    }
}

void RecvMessageCan()
{

    while (1)
    {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []() { return ready; });
        //cout << "Hello from RecvMessageCan thread " << this_thread::get_id() << endl;
        //std::this_thread::sleep_for(std::chrono::seconds(2));
        // simulate expensive operation
   // Holds a handle to the CAN channel
        canHandle hnd;
        // Status returned by the Canlib calls
        canStatus stat;
        // The CANlib channel number we would like to use
        int channel_number = 1;
        // The first thing we need to do is to initialize the Canlib library. This
        // always needs to be done before doing anything with the library.
        canInitializeLibrary();
        cout << "[Recving....]Opening channel-->" << channel_number << endl;;
        // Next, we open up the channel and receive a handle to it. Depending on what
        // devices you have connected to your computer, you might want to change the
        // channel number. The canOPEN_ACCEPT_VIRTUAL flag means that it is ok to
        // open the selected channel, even if it is on a virtual device.
        
        //CAN
        //hnd = canOpenChannel(channel_number, canOPEN_ACCEPT_VIRTUAL);

        //CAN FD
        hnd = canOpenChannel(channel_number, canOPEN_ACCEPT_VIRTUAL | canOPEN_CAN_FD | canOPEN_EXCLUSIVE | canOPEN_REQUIRE_EXTENDED);
        // If the call to canOpenChannel is successful, it will return an integer
        // which is greater than or equal to zero. However, is something goes wrong,
        // it will return an error status which is a negative number.
        if (hnd < 0) {
            // To check for errors and print any possible error message, we can use the
            // Check method.
            Check("canOpenChannel", (canStatus)hnd);
            // and then exit the program.
            exit(1);
        }


        //CAN
        //stat = canSetBusParams(hnd, canBITRATE_250K, 0, 0, 0, 0, 0);

        //CAN FD
        // set the arbitration bitrate to 500 kbit/s, with sampling point to 80%,
        // and data phase bitrate to 1000 kbit/s, with sampling point at 80%
        stat = canSetBusParams(hnd, canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0);
        Check("canSetBusParams", stat);
        stat = canSetBusParamsFd(hnd, canFD_BITRATE_1M_80P, 0, 0, 0);
        Check("canSetBusParamsFD", stat);
        ////////////
        Check("canSetBusParams", stat);
        // Next, take the channel on bus using the canBusOn method. This needs to be
        // done before we can read any messages from the bus.
        stat = canBusOn(hnd);
        Check("canBusOn", stat);
        // Listening for messages
        dumpMessageLoop(hnd, channel_number);
        //printf("Going of bus and closing channel");
        // Once we are done using the channel, we go off bus using the
        // canBusOff method. It take the handle as the only argument.
        stat = canBusOff(hnd);
        Check("canBusOff", stat);
        // We also close the channel using the canCloseChannel method, which take the
        // handle as the only argument.
        stat = canClose(hnd);
        Check("canClose", stat);
        //cout << "[Recv Successfully..........]" << endl << endl;
    }

}

int main()
{
    std::cout << "starting first helper...\n";
    std::thread helper1(SendMessageCan);

    std::cout << "starting second helper...\n";
    std::thread helper2(RecvMessageCan);
    std::cout << "waiting for helpers to finish..." << std::endl;
    helper1.join();
    helper2.join();
    std::cout << "done!\n";
}
